package com.example.myweb.Security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Component // 声明为Spring组件，以便注入
public class JwtUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private long expiration; // JWT的有效时间，毫秒

    private SecretKey signingKey;

    @PostConstruct
    public void init() {
        // 推荐使用 UTF-8 编码的纯文本密钥，然后通过 HMAC SHA-256 生成 SecretKey
        // 确保你的 application.properties 中的 jwt.secret 是一个足够长的随机字符串（例如32个字符以上）
        byte[] secretBytes = secret.getBytes(StandardCharsets.UTF_8);
        this.signingKey = Keys.hmacShaKeyFor(secretBytes);
        System.out.println("JwtUtil initialized with signing key: " + signingKey.getAlgorithm());
    }

    private Key getSignInKey() {
        return signingKey;
    }

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parser()// 这个方法现在应该能解析了
                .setSigningKey(getSignInKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    // ======================== 重点修改部分 ========================

    // 检查JWT是否过期 (确保是 public 访问权限)
    public boolean isTokenExpired(String token) {
        try {
            return extractExpiration(token).before(new Date());
        } catch (ExpiredJwtException e) {
            // 如果在提取过期时间时就抛出 ExpiredJwtException，说明它肯定过期了
            System.out.println("DEBUG JwtUtil: Token " + token.substring(0, 20) + "... is expired by ExpiredJwtException.");
            return true;
        } catch (Exception e) {
            // 其他异常（如签名无效）导致无法提取过期时间，也视为无效或过期
            System.err.println("DEBUG JwtUtil: Error checking token expiration for " + token.substring(0, 20) + "...: " + e.getMessage());
            return true;
        }
    }

    // 提取JWT过期时间 (确保是 public 访问权限)
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    // 验证JWT是否有效 (不依赖UserDetails，用于初步验证，例如登出时)
    // 这个方法现在应该没有“应为2个实参”的问题了，因为它就接收1个参数
    public Boolean validateToken(String token) {
        try {
            // 尝试解析并验证签名，如果成功且未过期，则认为有效
            Jwts.parser()
                    .setSigningKey(getSignInKey())
                    .build()
                    .parseClaimsJws(token);
            // 如果解析成功，还需要额外检查是否真的过期了 (虽然parseClaimsJws会抛出ExpiredJwtException)
            // 这里的 isTokenExpired 主要是为了在没有抛出 ExpiredJwtException 但时间已过的情况下确保
            boolean expired = isTokenExpired(token);
            if (expired) {
                System.out.println("DEBUG JwtUtil: Token " + token.substring(0, 20) + "... is structurally valid but EXPIRED.");
                return false;
            }
            System.out.println("DEBUG JwtUtil: Token " + token.substring(0, 20) + "... is VALID (simple check).");
            return true;
        } catch (ExpiredJwtException e) {
            System.out.println("DEBUG JwtUtil: Token " + token.substring(0, 20) + "... is EXPIRED: " + e.getMessage());
            return false;
        } catch (SignatureException e) {
            System.out.println("DEBUG JwtUtil: Token " + token.substring(0, 20) + "... has INVALID SIGNATURE: " + e.getMessage());
            return false;
        } catch (IllegalArgumentException e) {
            System.out.println("DEBUG JwtUtil: Token " + token.substring(0, 20) + "... has INVALID STRUCTURE/Claims: " + e.getMessage());
            return false;
        } catch (Exception e) {
            System.err.println("DEBUG JwtUtil: Token " + token.substring(0, 20) + "... validation FAILED with unexpected error: " + e.getClass().getSimpleName() + " - " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    // 验证JWT是否有效 (依赖UserDetails，用于SecurityContext认证)
    // 这个方法现在应该没有“需要类型: String 提供类型: UserDetails”的问题了
    public boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        boolean isValid = (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
        if (!isValid) {
            System.out.println("DEBUG JwtUtil: Token " + token.substring(0, 20) + "... validation failed for user " + username + ". Expired: " + isTokenExpired(token) + ", User match: " + username.equals(userDetails.getUsername()));
        } else {
            System.out.println("DEBUG JwtUtil: Token " + token.substring(0, 20) + "... valid for user " + username + ".");
        }
        return isValid;
    }

    // 生成JWT
    public String generateToken(UserDetails userDetails) {
        return generateToken(new HashMap<>(), userDetails);
    }

    // 生成JWT (带额外声明)
    public String generateToken(Map<String, Object> extraClaims, UserDetails userDetails) {
        return Jwts.builder()
                .setClaims(extraClaims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiration))
                .signWith(getSignInKey()) // 明确指定算法
                .compact();
    }
}